package com.ruoyi.util

import cn.hutool.core.util.StrUtil
import com.alibaba.fastjson.JSON
import com.aliyun.com.viapi.FileUtils
import com.aliyun.facebody20191230.models.AddFaceImageTemplateRequest
import com.aliyun.facebody20191230.models.MergeImageFaceRequest
import com.aliyun.tea.TeaException
import com.aliyun.teaopenapi.models.Config
import com.aliyun.viapi20230117.models.GetAsyncJobResultRequest
import com.aliyun.videoenhan20200320.models.AddFaceVideoTemplateRequest
import com.aliyun.videoenhan20200320.models.MergeVideoModelFaceRequest
import com.ruoyi.app.common.Sign
import com.ruoyi.app.domain.dto.ImageChangeFaceDto
import com.ruoyi.app.domain.dto.VideoChangeFaceDto
import com.ruoyi.back.domain.Template
import com.ruoyi.back.domain.v3.PicTemplateFace
import com.ruoyi.back.domain.v3.VideoTemplateFace
import com.ruoyi.exception.FlyException
import org.slf4j.Logger
import org.slf4j.LoggerFactory

const val ACCESS_KEY: String = "LTAI5tRQ1acQfyPAnkCWvMAd"
const val ACCESS_SECRET: String = "SVVk0Non9jDhpSdISorUnfYZFBqs9c"
const val MUL_TI: String = "multi"

val log: Logger = LoggerFactory.getLogger(Template::class.java)

fun String.upload(): String = FileUtils.getInstance(ACCESS_KEY, ACCESS_SECRET).upload(this)


data class TemplateVideoResult(
    var templateId: String? = null,
    var faceInfos: List<VideoFaceInfos>? = null
)

data class VideoFaceInfos(
    var templateFaceID: String? = null,
    var templateFaceURL: String? = null
)

data class TemplatePicFaceResult(
    var templateId: String? = null,
    var faceInfos: List<PicFaceInfos>? = null
)

data class PicFaceInfos(
    var templateFaceID: String? = null,
    var faceRect: FaceRect? = null
)

data class FaceRect(
    var X: String? = null,
    var Y: String? = null,
    var Height: String? = null,
    var Width: String? = null
)

enum class AsyncStatus(val status: String) {

    /**
     * 任务排队中
     */
    QUEUING("QUEUING"),

    /**
     * 异步处理中
     */
    PROCESSING("PROCESSING"),

    /**
     * 处理成功
     */
    PROCESS_SUCCESS("PROCESS_SUCCESS"),

    /**
     * 处理失败
     */
    PROCESS_FAILED("PROCESS_FAILED"),

    /**
     * 任务超时未处理完成
     */
    TIMEOUT_FAILED("TIMEOUT_FAILED"),

    /**
     * 超过最大重试次数
     */
    LIMIT_RETRY_FAILED("LIMIT_RETRY_FAILED")
}


val client: () -> com.aliyun.videoenhan20200320.Client = {
    com.aliyun.videoenhan20200320.Client(Config().apply {
        accessKeyId = ACCESS_KEY
        accessKeySecret = ACCESS_SECRET
        endpoint = "videoenhan.cn-shanghai.aliyuncs.com"
    })
}

val AsyncClient: () -> com.aliyun.viapi20230117.Client = {
    com.aliyun.viapi20230117.Client(Config().apply {
        accessKeyId = ACCESS_KEY
        accessKeySecret = ACCESS_SECRET
        endpoint = "viapi.cn-shanghai.aliyuncs.com"
    })
}

val PicClient: () -> com.aliyun.facebody20191230.Client = {
    com.aliyun.facebody20191230.Client(Config().apply {
        accessKeyId = ACCESS_KEY
        accessKeySecret = ACCESS_SECRET
        endpoint = "facebody.cn-shanghai.aliyuncs.com"
    })
}


fun addPicTemplate(url: String, picResolve: ((List<PicTemplateFace>?, String) -> Unit)?) {
    PicClient().addFaceImageTemplate(AddFaceImageTemplateRequest().apply {
        imageURL = url.upload()
    }).let { result ->
        if (result.getStatusCode() != 200) {
            throw FlyException("上传图片模板发生异常!")
        } else {
            result.body.data.let { data ->
                picResolve?.run {
                    picResolve(data.faceInfos?.takeIf { it.isNotEmpty() }
                        ?.run {
                            map {
                                PicTemplateFace().apply {
                                    templateId = data.templateId
                                    templateFaceId = it.templateFaceID
                                    xPoint = it.faceRect.x
                                    yPoint = it.faceRect.y
                                    height = it.faceRect.height
                                    width = it.faceRect.width
                                }
                            }
                        }, data.templateId)
                }
            }
        }
    }
}

val changePicFace: (ImageChangeFaceDto, String?) -> String? = { imageParam, picTemplateId ->
    PicClient().let { client ->
        imageParam.localTemplateUrl.takeIf { StrUtil.isNotBlank(it) }?.let { localPic ->

            var picUseTemplateId: String? = null
            addPicTemplate(localPic) { _, templateId ->
                picUseTemplateId = templateId
            }
            try {
                client.mergeImageFace(MergeImageFaceRequest().apply {
                    imageURL = imageParam.imageUrl?.upload()
                    templateId = picUseTemplateId
                    addWatermark = imageParam.aiWatermark
                    watermarkType = "CN"
                }).let { result ->
                    when (result.getStatusCode()) {
                        400 -> throw FlyException("无效的图片类型。")
                        404 -> throw FlyException("图像中未找到人脸，请检查您的图像中是否包含人脸或人脸太小。")
                        449 -> throw FlyException("由于网络抖动等偶发原因导致失败，需要重试。")
                        500 -> throw FlyException("网络请求错误。请优先检查本地网络是否存在故障。")
                        else -> {}
                    }
                    result.getBody().getData().getImageURL()
                }
            } catch (e: TeaException) {
                throw FlyException(e.message)
            }
        } ?: run {

            imageParam.takeIf { StrUtil.isNotBlank(it.faceId) || StrUtil.isNotBlank(it.faces) }?.run {
                try {
                    client.mergeImageFace(MergeImageFaceRequest().apply {
                        templateId = picTemplateId
                        mergeInfos = imageParam.faceId?.run {
                            listOf(MergeImageFaceRequest.MergeImageFaceRequestMergeInfos().apply {
                                imageURL = imageParam.imageUrl?.upload()
                                templateFaceID = imageParam.faceId
                            })
                        } ?: run {
                            JSON.parseArray(imageParam.faces, Face::class.java)
                                .map {
                                    MergeImageFaceRequest.MergeImageFaceRequestMergeInfos().apply {
                                        imageURL = it.imageUrl?.upload()
                                        templateFaceID = it.faceId
                                    }
                                }
                        }
                        addWatermark = imageParam.aiWatermark
                        watermarkType = "CN"
                    }).let { result ->
                        when (result.getStatusCode()) {
                            400 -> throw FlyException("无效的图片类型。")
                            404 -> throw FlyException("图像中未找到人脸，请检查您的图像中是否包含人脸或人脸太小。")
                            449 -> throw FlyException("由于网络抖动等偶发原因导致失败，需要重试。")
                            500 -> throw FlyException("网络请求错误。请优先检查本地网络是否存在故障。")
                            else -> {}
                        }
                        result.getBody().getData().getImageURL()
                    }
                } catch (e: TeaException) {
                    throw FlyException(e.message)
                }
            } ?: run {
                try {
                    client.mergeImageFace(MergeImageFaceRequest().apply {
                        templateId = picTemplateId
                        imageURL = imageParam.imageUrl?.upload()
                        addWatermark = imageParam.aiWatermark
                        watermarkType = "CN"
                    }).let { result ->
                        when (result.getStatusCode()) {
                            400 -> throw FlyException("无效的图片类型。")
                            404 -> throw FlyException("图像中未找到人脸，请检查您的图像中是否包含人脸或人脸太小。")
                            449 -> throw FlyException("由于网络抖动等偶发原因导致失败，需要重试。")
                            500 -> throw FlyException("网络请求错误。请优先检查本地网络是否存在故障。")
                            else -> {}
                        }
                        result.getBody().getData().getImageURL()
                    }
                } catch (e: TeaException) {
                    throw FlyException(e.message)
                }
            }

        }
    }
}


val addVideoTemplate: (String) -> String = {

    try {
        client().addFaceVideoTemplate(
            AddFaceVideoTemplateRequest()
                .apply {
                    videoURL = it.upload()
                    videoScene = MUL_TI
                }
        ).getBody().requestId

    } catch (e: TeaException) {
        throw FlyException(e.message)
    }
}

data class ChangeVideoParam(
    var templateId: String? = null,
    var faces: List<Face>? = null,
    var faceId: String? = null,
    var imageUrl: String? = null,
    var aiWatermark: Boolean? = false,
)

data class Face(
    var faceId: String? = null,
    var imageUrl: String? = null
)

val changeVideoTemplateFace: (ChangeVideoParam) -> String? = {
    try {
        client().mergeVideoModelFace(
            MergeVideoModelFaceRequest().apply {
                templateId = it.templateId
                mergeInfos = it.faces?.run {
                    map {
                        MergeVideoModelFaceRequest.MergeVideoModelFaceRequestMergeInfos()
                            .apply {
                                templateFaceID = it.faceId
                                imageURL = it.imageUrl?.takeIf { it.isNotBlank() }?.upload()
                            }
                    }
                } ?: run {
                    listOf(
                        MergeVideoModelFaceRequest.MergeVideoModelFaceRequestMergeInfos()
                            .apply {
                                templateFaceID = it.faceId
                                imageURL = it.imageUrl
                            }
                    )
                }
                addWatermark = it.aiWatermark
                watermarkType = "CN"
                enhance = true

            })
            .let { result ->
                if (result.getStatusCode() != 200) throw FlyException("模板视频融合人脸失败")
                result.getBody().requestId
            }
    } catch (e: TeaException) {
        throw FlyException(e.message)
    }
}


val changeVideoFace: (VideoChangeFaceDto, String?, String?) -> String? = { videoParam, videoTemplateId, videoContent  ->
    videoParam.localTemplateUrl.takeIf { StrUtil.isNotBlank(it) }?.let { localUrl ->
        Sign.requestChangeFace(localUrl.upload(), "", videoParam.referenceUrl?.upload(), videoParam.aiWatermark)
    } ?: run {
        videoParam.takeIf { StrUtil.isNotBlank(it.faces) || StrUtil.isNotBlank(it.faceId) }
            ?.run {
                videoTemplateId ?: throw FlyException("请传递模板id")
                changeVideoTemplateFace(ChangeVideoParam().apply {
                    templateId = videoTemplateId
                    faces = JSON.parseArray(videoParam.faces, Face::class.java)
                    faceId = videoParam.faceId
                    imageUrl = videoParam.referenceUrl?.takeIf { it.isNotBlank() }?.upload()
                    aiWatermark = videoParam.aiWatermark
                })
            }?: run {
            Sign.requestChangeFace(
                videoContent?.upload(),
                "",
                videoParam.referenceUrl?.upload(),
                videoParam.aiWatermark
            )
        }
    }
}

val asyncAddVideoTemplateResult: (String) -> List<VideoTemplateFace>? = {
    try {
        AsyncClient().getAsyncJobResult(GetAsyncJobResultRequest().apply {
            jobId = it
        }).let { result ->
            if (result.getStatusCode() != 200) {
                throw FlyException("上传视频模板发生异常!")
            } else {
                result.getBody().getData().let { data ->
                    if (data.getStatus() == AsyncStatus.PROCESS_SUCCESS.status) {
                        log.info("${result.getBody().getRequestId()}处理成功")
                        JSON.parseObject(data.result, TemplateVideoResult::class.java)
                            .let { result ->
                                result.faceInfos?.run {
                                    map {
                                        VideoTemplateFace().apply {
                                            templateId = result.templateId
                                            templateFaceId = it.templateFaceID
                                            templateFaceUrl = it.templateFaceURL
                                        }
                                    }
                                }
                            }
                    } else null
                }
            }
        }
    } catch (e: TeaException) {
        throw FlyException(e.message)
    } catch (e: Exception) {
        throw e
    }
}


fun main() {
    //println(addVideoTemplate("https://shuiyin-program.oss-cn-hangzhou.aliyuncs.com//userfiles/20230324162944.mp4"))

    //println(asyncAddVideoTemplateResult("822B75DF-18C6-5A72-8F05-829F412B5738"))

    println("https://shuiyin-program.oss-cn-hangzhou.aliyuncs.com//userfiles/20230327171821.jpg".upload())
}